package alf.mapping.data;
import alf.mapping.dto.maps.Item;
import alf.mapping.dto.maps.MapModel;
import alf.mapping.dto.maps.NavigationLink;
import alf.mapping.dto.maps.Room;
import alf.mapping.view.Env;
import android.graphics.Rect;
import android.util.Xml;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import alf.mapping.dto.maps.Link;

// ejemplo:

//<map mod="default">
//	<room name="room1" mod="office" terrain="default">
//		<item name="mesa1" x="500" y="500" height="64" width="64" type="mesa" link="room2">Esta mesa es muy importante</item>
//		<item name="silla" x="500" y="532" height="64" width="64" type="silla" />
//	</room>
//	<room name="room2" mod="office" terrain="default">
//		<item name="otra mesa" x="500" y="500" height="64" width="64" type="mesa">Esta mesa tampoco es importante</item>
//		<item name="silla2" x="500" y="532" height="64" width="64" type="silla" />
//	</room>
//</map>

public class MapParser {

	private static final String NAMESPACE = null;
	private Map<String,Room> roomCache;
	private Map<String,Item> itemCache;

	public MapModel parse(InputStream s) throws XmlPullParserException, IOException {
		try {
			XmlPullParser parser =  Xml.newPullParser();
			parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
			parser.setInput(s, null);
			parser.nextTag();
			return readMap(parser);
		} finally {
			s.close();
		}
	}

	private MapModel readMap(XmlPullParser parser) throws IOException, XmlPullParserException {
		Env.debug("readMap");
		resetCaches();
		MapModel map = new MapModel();
		parser.require(XmlPullParser.START_TAG, NAMESPACE, "map");
		while (parser.next() != XmlPullParser.END_TAG) {
			if (parser.getEventType() != XmlPullParser.START_TAG) {
				continue;
			}
			String tag = parser.getName();
			if (tag.equalsIgnoreCase("room")) {
				map.addRoom(readRoom(parser));
			} else {
				skip(parser);
			}
		}
		Env.debug("readMap.end");
		return map;
	}

	private void resetCaches() {
		roomCache = new HashMap<String,Room>();
		itemCache = new HashMap<String,Item>();
	}

	private Room readRoom(XmlPullParser parser) throws IOException, XmlPullParserException {
		Env.debug("readRoom");
		parser.require(XmlPullParser.START_TAG, NAMESPACE, "room");
		String name = getAttr(parser, "name", null);
		Room r = getRoom(name);
		r.setMod(getAttr(parser, "mod", "default"));
		r.setTerrain(getAttr(parser, "terrain", "default"));
		while (parser.next() != XmlPullParser.END_TAG) {
			if (parser.getEventType() != XmlPullParser.START_TAG) {
				continue;
			}
			String tag = parser.getName();
			if (tag.equalsIgnoreCase("item")) {
				r.addEntity(readItem(parser, name));
			} else {
				skip(parser);
			}
		}
		return r;
	}

	private Room getRoom(String name) {
		Room r = roomCache.get(name);
		if (r == null) {
			r = new Room();
			r.setName(name);
			roomCache.put(name, r);
		}
		return r;
	}

	private String getAttr(XmlPullParser parser, String name, String defaultVal) {
		String r = null;
		r = parser.getAttributeValue(NAMESPACE, name);
		if (r == null) {
			r = defaultVal;
		}
		return r;
	}

	private Item readItem(XmlPullParser ps, String roomName) throws IOException, XmlPullParserException {
		Env.debug("readItem");
		int x,y,w,h;

		ps.require(XmlPullParser.START_TAG, NAMESPACE, "item");
		String name = getAttr(ps, "name", null);
		Item i = getItem(name, roomName);

		i.setType(getAttr(ps, "type", null));
		x = Integer.parseInt(getAttr(ps, "x", "0"));
		y = Integer.parseInt(getAttr(ps, "y", "0"));
		w = Integer.parseInt(getAttr(ps, "width", "0"));
		h = Integer.parseInt(getAttr(ps, "height", "0"));
	
		i.setRect(new Rect(x, y, x + w, y + h));
		int eventType = ps.next();
		while (eventType != XmlPullParser.END_TAG) {
			if (ps.getEventType() != XmlPullParser.START_TAG) {
				eventType = ps.next();
				continue;
			}
			String tag = ps.getName();
			if (tag.equalsIgnoreCase("link")) {
				i.add(readLink(ps, roomName));
			} else if (tag.equalsIgnoreCase("text")){
				i.setText(readText(ps));
			} else {
				skip(ps);
			}
		}
		Env.debug("ReadItem.end "+ debug( ps));
	return i;
	}
	
	private String debug(XmlPullParser p) throws XmlPullParserException  {
		String tag = p.getName();
		String msg = "?";
		switch(p.getEventType()){
			case XmlPullParser.START_TAG:
				msg = "<"+ tag + "...";
				break;
			case XmlPullParser.END_TAG:
				msg = "</" + tag + ">";
				break;
			case XmlPullParser.END_DOCUMENT:
				msg = "[End document]";
				break;
		}
		return msg;
	}


	private Link readLink(XmlPullParser ps, String roomName) throws IOException, XmlPullParserException {
		Env.debug("readLink");

		ps.require(XmlPullParser.START_TAG, NAMESPACE, "link");
		Link link = new Link();
		link.setTitle(getAttr(ps, "title", null));
		String dest = getAttr(ps, "dest", null);
		link.setDestination(processLinkValue(roomName, dest));
		link.setNavigable(Boolean.parseBoolean(getAttr(ps, "navigable", "false")));
		link.setVisible(Boolean.parseBoolean(getAttr(ps, "visible", "false")));
		link.setStyle(getAttr(ps, "style", null));
		
		while (ps.next() != XmlPullParser.END_TAG) {
			if (ps.getEventType() != XmlPullParser.START_TAG) {
				continue;
			}
			skip(ps);
		}
		Env.debug("readLink.end "+ debug(ps));
		return link;
	}

	private String readText(XmlPullParser ps) throws XmlPullParserException, IOException {
		Env.debug("readText");
		String value = null;
		ps.require(XmlPullParser.START_TAG, NAMESPACE, "text");
			if (ps.next() == XmlPullParser.TEXT) {
				value = ps.getText();
				ps.nextTag();
			} 
			
	
		Env.debug("readText.end " + debug(ps));
		return value;
	}

	private NavigationLink processLinkValue(String roomName, String newLink) {
		NavigationLink value = null;

		String diagramPart, itemPart;
		if (newLink == null || newLink.equals("") || newLink.equals("/")) {
			return null;
		}
		if (newLink.contains("/")) {
			int pos = newLink.indexOf("/");
			diagramPart = newLink.substring(0, pos);
			itemPart = newLink.substring(pos + 1);
		} else {
			diagramPart = newLink;
			itemPart = null;
		}
		value = new NavigationLink();
		if (diagramPart != null) {
			value.setDiagram(getRoom(diagramPart));
		}
		if (itemPart != null) {
			value.setItem(getItem(itemPart, diagramPart));
		}
		return value;
	}

	private Item getItem(String name, String roomName) {
		String path = roomName + "/" + name;
		Item i = itemCache.get(path);
		if (i == null) {
			i = new Item();
			i.setName(name);
			itemCache.put(path, i);
		}
		return i;
	}	

	private void skip(XmlPullParser parser) throws IllegalStateException, XmlPullParserException, IOException {
		if (parser.getEventType() != XmlPullParser.START_TAG) {
			throw new IllegalStateException();
		}
		int depth =1;
		while (depth != 0) {
			switch (parser.next()) {
				case XmlPullParser.END_TAG:
					depth--;
					break;
				case XmlPullParser.START_TAG:
					depth++;
					break;
			}
		}
	}
}
